package lq.gateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import lq.common.entity.StudyConstant;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import javax.servlet.http.HttpServletRequest;

/**
 * 微服务防护
 * 实现思路:
 * 自定义zuul过滤器
 * 在网关转发请求前,请求头部加上网关信息,
 * 然后在处理请求的微服务模块里面定义全局拦截器,校验请求头部的网关信息
 * 这样就能避免客服端直接访问微服务了
 *
 * @program: cloud-study
 * @description: 自定义zuul过滤器
 * @author: LQ
 * @create: 2021-01-25 16:09
 */
@Slf4j
@Component
public class StudyGatewayRequestFilter extends ZuulFilter {
    @Override
    public String filterType() {
        /*
         *PRE：PRE过滤器用于将请求路径与配置的路由规则进行匹配，以找到需要转发的目标地址，并做一些前置加工，比如请求的校验等；
         *ROUTING：ROUTING过滤器用于将外部请求转发到具体服务实例上去；
         *POST：POST过滤器用于将微服务的响应信息返回到客户端，这个过程种可以对返回数据进行加工处理；
         *ERROR：上述的过程发生异常后将调用ERROR过滤器。ERROR过滤器捕获到异常后需要将异常信息返回给客户端，所以最终还是会调用POST过滤器。
         *
         * filterType，对应Zuul生命周期的四个阶段：pre、post、route和error，我们要在请求转发出去前添加请求头，所以这里指定为pre；
         */
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        /*
         * filterOrder，过滤器的优先级，数字越小，优先级越高。
         * PreDecorationFilter用于处理请求上下文，优先级为5
         * 定义一个优先级在PreDecorationFilter之后的过滤器，便可以拿到请求上下文。
         *
         */
        return 6;
    }

    @Override
    public boolean shouldFilter() {
        /*
         * 方法返回boolean类型，true时表示是否执行该过滤器的run方法，false则表示不执行
         */
        return true;
    }

    /**
     * run，定义过滤器的主要逻辑
     * 通过请求上下文RequestContext获取了转发的服务名称serviceId和请求对象HttpServletRequest，并打印请求日志。
     * 随后往请求上下文的头部添加了Key，Value
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        String serviceId = (String) currentContext.get(FilterConstants.SERVICE_ID_KEY);
        HttpServletRequest request = currentContext.getRequest();
        String host = request.getRemoteHost();
        String method = request.getMethod();
        String uri = request.getRequestURI();
        log.info("请求URI：{}，HTTP Method：{}，请求IP：{}，ServerId：{}", uri, method, host, serviceId);
        byte[] token = Base64Utils.encode(StudyConstant.ZUUL_TOKEN_VALUE.getBytes());
        currentContext.addZuulRequestHeader(StudyConstant.ZUUL_TOKEN_HEADER, new String(token));
        return null;
    }
}
